/*
 * Copyright 2013, Edmodo, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with
 * the License.
 * You may obtain a copy of the License in the LICENSE file, or at:
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */

package com.appyvet.rangebarsample.colorpicker;

import com.appyvet.rangebarsample.ComponentColor;
import com.appyvet.rangebarsample.utils.LogUtil;
import com.appyvet.rangebarsample.ResourceTable;
import com.appyvet.rangebarsample.utils.ResUtil;

import ohos.app.Context;

import ohos.agp.components.DirectionalLayout;
import ohos.agp.components.Component;
import ohos.agp.components.TableLayout;
import ohos.agp.components.Text;
import ohos.agp.components.LayoutScatter;
import ohos.agp.components.ComponentContainer;
import ohos.agp.utils.Color;
import ohos.agp.window.dialog.CommonDialog;

import ohos.global.configuration.Configuration;
import ohos.global.resource.solidxml.Theme;

import java.io.Serializable;
import java.util.Locale;

/**
 * This class is responsible to create color
 * picker dialog having different color circles
 */
public class ColorPickerDialog extends CommonDialog implements Component.ClickedListener {
    private static final String TAG = ColorPickerDialog.class.getSimpleName();
    private Color[] colorsTop;
    private Color[][] colorsSub;
    private DirectionalLayout mDialogLayout;
    private Context mContext;
    private TableLayout tableLayout;
    private DirectionalLayout.LayoutConfig cvlayoutConfig;
    private boolean isInSub = false;
    private int topIndex = 0;
    private int subIndex = 0;
    private Text title;

    /**
     * OnColorSelectedListener interface called
     * whenever color is selected in
     * color picker dialog
     */
    protected OnColorSelectedListener mListener;

    public ColorPickerDialog(Context context) {
        super(context);
        mContext = context;
        generateColors();
        initDialogLayout();
    }

    private void generateColors() {
        Builder builder = getBuilder();

        if (builder.colorsTop != null) {
            colorsTop = builder.colorsTop;
            colorsSub = builder.colorsSub;
            return;
        }

        if (builder.accentMode) {
            colorsTop = ColorPickerPalette.ACCENT_COLORS;
            colorsSub = ColorPickerPalette.ACCENT_COLORS_SUB;
        } else {
            colorsTop = ColorPickerPalette.PRIMARY_COLORS;
            colorsSub = ColorPickerPalette.PRIMARY_COLORS_SUB;
        }
    }

    private void initDialogLayout() {
        mDialogLayout = (DirectionalLayout) LayoutScatter.getInstance(mContext)
                .parse(ResourceTable.Layout_rangebar_dialog_colorchooser,
                        null, false);

        if (!(mDialogLayout instanceof ComponentContainer)) {
            return;
        }

        boolean foundPreselectColor = false;

        title = (Text) mDialogLayout.findComponentById(ResourceTable.Id_rangeBar_title);
        title.setText(getTitle());

        createTableLayout();
        if (getBuilder().allowUserCustom) {
            if (!foundPreselectColor) {
                // If color wasn't found in the preset colors, it must be custom
                toggleCustom();
            }
        }
        setContentCustomComponent(mDialogLayout);
    }

    private void toggleCustom() {
            title.setText(getBuilder().title);
            tableLayout.setVisibility(Component.VISIBLE);
    }

    private int topIndex() {
        return topIndex;
    }

    private void topIndex(int value) {
        if (value > -1) {
            findSubIndexForColor(value, colorsTop[value]);
        }
        topIndex = value;
    }

    private void findSubIndexForColor(int topIndex, Color color) {
        if (colorsSub == null || topIndex > colorsSub.length - 1) {
            return;
        }
        Color[] subColors = colorsSub[topIndex];
        for (int i = 0; i < subColors.length; i++) {
            if (subColors[i].getValue() == color.getValue()) {
                subIndex(i);
                break;
            }
        }
    }

    private int subIndex() {
        if (colorsSub == null) {
            return -1;
        }
        return subIndex;
    }

    private void subIndex(int value) {
        if (colorsSub == null) {
            return;
        }
        subIndex = value;
    }

    private int getTitle() {
        Builder builder = getBuilder();
        int builderTitle;
        if (isInSub()) {
            builderTitle = builder.titleSub;
        } else {
            builderTitle = builder.title;
        }
        if (builderTitle == 0) {
            builderTitle = builder.title;
        }
        return builderTitle;
    }

    private void isInSub(boolean value) {
        isInSub = value;
    }

    private boolean isInSub() {
        return isInSub;
    }

    private int getCount() {
        if (isInSub()) {
            return colorsSub[topIndex()].length;
        } else {
            return colorsTop.length;
        }
    }

    private void prepareCircleViews() {
        for (int position = 0; position < getCount(); position++) {
            CircleView cv = new CircleView(mContext);
            cv.setLayoutConfig(cvlayoutConfig);
            tableLayout.addComponent((Component) cv);
            Color color = isInSub() ? colorsSub[topIndex()][position] : colorsTop[position];
            cv.setBackgroundColor(color);

            if (isInSub()) {
                cv.setSelected(subIndex() == position);
            } else {
                cv.setSelected(topIndex() == position);
            }
            cv.setTag(String.format(Locale.ROOT, "%d:%d", position, color.getValue()));
            cv.setClickedListener(new Component.ClickedListener() {
                @Override
                public void onClick(Component component) {
                    if (mListener != null) {
                        mListener.onColorSelected(color.getValue(), Builder.getBuilder().getComponentResource());
                    }
                    invalidate();
                    mListener.onColorChooserDismissed(getBuilder().build());
                    getBuilder().dismiss();
                }
            });

            cv.setLongClickedListener(new Component.LongClickedListener() {
                @Override
                public void onLongClicked(Component component) {
                    if (component.getTag() != null) {
                        final String[] tag = ((String) component.getTag()).split(":");
                        try {
                            final int color = Integer.parseInt(tag[1]);
                            ((CircleView) component).showHint(color);
                        } catch (NumberFormatException e) {
                            LogUtil.error(TAG, e.getMessage());
                        }
                    }
                }
            });
        }
    }

    private void invalidate() {
        // Remove previous components
        tableLayout.removeAllComponents();
        prepareCircleViews();
        if (title != null) {
            title.setText(getTitle());
        }
    }

    private int getRowCount() {
        if (isInSub()) {
            return 3;
        } else {
            return 5;
        }
    }

    private int getColumnCount() {
        if (isInSub()) {
            return 4;
        } else {
            return 4;
        }
    }

    private TableLayout createTableLayout() {
        tableLayout = (TableLayout) mDialogLayout.findComponentById(ResourceTable.Id_rangeBar_table_layout);
        ComponentContainer.LayoutConfig layoutConfig =
                new ComponentContainer.LayoutConfig(ComponentContainer.LayoutConfig.MATCH_CONTENT,
                ComponentContainer.LayoutConfig.MATCH_CONTENT);
        tableLayout.setRowCount(getRowCount());
        tableLayout.setColumnCount(getColumnCount());
        tableLayout.verifyLayoutConfig(layoutConfig);

        int width = (int) ResUtil.getDimen(mContext, ResourceTable.Float_md_colorchooser_circlesize);
        int height = (int) ResUtil.getDimen(mContext, ResourceTable.Float_md_colorchooser_circlesize);
        ComponentContainer.LayoutConfig cvlayout = new ComponentContainer.LayoutConfig(width, height);
        cvlayout.setMargins(20, 20, 20, 20);
        cvlayoutConfig = new DirectionalLayout.LayoutConfig(cvlayout);
        prepareCircleViews();

        return tableLayout;
    }

    @Override
    protected void onCreate() {
        super.onCreate();
        super.setSwipeToDismiss(true);
        setContentCustomComponent(mDialogLayout);
    }

    /**
     * Used to return color picker dialog builder object
     *
     * @return Builder the color picker builder object
     */
    private Builder getBuilder() {
        return Builder.getBuilder();
    }

    @Override
    protected void onHide() {
        super.onHide();
        if (mListener != null) {
            mListener.onColorChooserDismissed(this);
        }
    }

    @Override
    public void onClick(Component component) { }

    /**
     * Builder class for color picker dialog
     */
    public static class Builder implements Serializable {
        /**
         * Builder to create objects in series taking
         * input from user for color picker dialog
         */
        public static Builder mbuilder;

        ComponentColor mComponentResource;
        Context context;
        String mediumFont;
        String regularFont;
        final int title;
        int titleSub;
        int preselectColor;
        Color[] colorsTop;
        Color[][] colorsSub;
        String tag;
        Theme theme;
        ColorPickerDialog dialog;

        boolean accentMode = false;
        boolean allowUserCustom = true;
        boolean setPreselectionColor = false;

        /**
         * Builder class parameterized constructor
         *
         * @param context the app context
         * @param title the builder title
         */
        public Builder(Context context, int title) {
            this.context = context;
            this.title = title;
            mbuilder = this;
        }

        /**
         * Used to returns color picker dialog builder.
         *
         * @return mBuilder color picker dialog builder
         */
        public static Builder getBuilder() {
            return mbuilder;
        }

        /**
         * Builder class for color picker dialog
         *
         * @param componentResource the enumeration indicates clicked component
         *
         * @return Builder the color picker Builder object
         */
        public Builder componentColor(ComponentColor componentResource) {
            mComponentResource = componentResource;
            return this;
        }

        /**
         * Use to return clicked component enumeration resource for choosing color
         *
         * @return mComponentResource component clicked resource
         */
        public ComponentColor getComponentResource() {
            return mComponentResource;
        }

        /**
         * Used to specify title of color
         * picker dialog
         *
         * @param titleSub title of color picker dialog
         * @return Builder the color picker dialog
         */
        public Builder titleSub(int titleSub) {
            this.titleSub = titleSub;
            return this;
        }

        /**
         * Used to specify tag of color
         * picker dialog
         *
         * @param tag color picker dialog tag
         * @return Builder the color picker dialog
         */
        public Builder tag(String tag) {
            this.tag = tag;
            return this;
        }

        /**
         * Used to set theme of the color picker dialog
         *
         * @param theme color picker dialog theme
         * @return Builder the color picker dialog
         */
        public Builder theme(Theme theme) {
            this.theme = theme;
            return this;
        }

        /**
         * Used to set selected color of color
         * picker dialog
         *
         * @param selectedColor the color picker dialog selected color
         * @return Builder the color picker dialog
         */
        public Builder selectedColor(int selectedColor) {
            preselectColor = selectedColor;
            setPreselectionColor = true;
            return this;
        }

        /**
         * Used to remove Color picker dialog
         *
         * @return ColorPickerDialog the object of ColorPickerDialog class
         */
        public ColorPickerDialog build() {
            this.context = context;
            ColorPickerDialog colorPickerDialog = new ColorPickerDialog(context);
            return colorPickerDialog;
        }

        /**
         * Used to show color picker dialog
         *
         * @return ColorPickerDialog the object of ColorPickerDialog class
         */
        public ColorPickerDialog show() {
            dialog = build();
            int orientation = context.getResourceManager().getConfiguration().direction;
            if (orientation == Configuration.DIRECTION_VERTICAL && dialog != null) {
                dialog.setSize(850, 1500);
            } else {
                dialog.setSize(850, 900);
            }
            dialog.show();
            return dialog;
        }

        /**
         * Used to remove Color picker dialog
         * after selecting particular color
         */
        public void dismiss() {
            if (dialog != null) {
                dialog.remove();
            }
        }
    }

    /**
     * Interface for a callback when a color square is selected.
     */
    public interface OnColorSelectedListener {
        /**
         * Called when a specific color square has been selected.
         *
         * @param color the selected color
         * @param resId the clicked component id
         */
        void onColorSelected(int color, ComponentColor resId);

        /**
         * Called when a color has been selected and color dialog get dismissed.
         *
         * @param dialog the color dialog
         */
        void onColorChooserDismissed(ColorPickerDialog dialog);
    }

    /**
     * Used to set OnColorSelectedListener passed from sample.
     *
     * @param listener the OnColorSelectedListener listener
     */
    public void setOnColorSelectedListener(OnColorSelectedListener listener) {
        mListener = listener;
    }
}
